/*! * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */

<template>
    <div id="app">
        <div>
            <h1>IAM Policy Checks</h1>
            <div v-if="!initialData.pythonToolsInstalled">
                <h3>Getting Started</h3>
                <p>
                    Policy Checks requires Python 3.6+ and the respective Python CLI tools installed, based on the
                    document type:
                </p>
                <ol>
                    <li>
                        <p>Install Python 3.6+</p>
                    </li>
                    <li>
                        <code> pip install cfn-policy-validator==0.0.36 </code>
                    </li>
                    <li>
                        <code> pip install tf-policy-validator==0.0.9 </code>
                    </li>
                    <li>
                        <p>Provide IAM Roles Credentials</p>
                    </li>
                </ol>
            </div>
            <div style="justify-content: space-between">
                <div style="display: flex">
                    <div style="display: block; margin-right: 25px">
                        <label for="select-document-type" style="display: block; margin-top: 5px; margin-bottom: 3px"
                            >Select a Document Type</label
                        >
                        <select id="select-document-type" v-on:change="setDocumentType" v-model="documentType">
                            <option value="CloudFormation">CloudFormation Template</option>
                            <option value="Terraform Plan">Terraform Plan</option>
                            <option value="JSON Policy Language">JSON Policy Language</option>
                        </select>
                    </div>
                    <div style="display: block" v-if="documentType == 'JSON Policy Language'">
                        <label for="select-policy-type" style="display: block; margin-top: 5px; margin-bottom: 3px"
                            >Policy Type</label
                        >
                        <select
                            id="select-policy-type"
                            v-on:change="setValidatePolicyType"
                            v-model="validatePolicyType"
                        >
                            <option value="Identity">Identity</option>
                            <option value="Resource">Resource</option>
                        </select>
                    </div>
                </div>
                <label for="input-path" style="display: block; cursor: not-allowed; margin-top: 15px; opacity: 0.4">
                    Open a file with the selected Document Type in the VS Code text editor to begin
                </label>
                <input
                    type="text"
                    style="
                        display: flex;
                        cursor: not-allowed;
                        box-sizing: border-box;
                        position: relative;
                        opacity: 0.4;
                        width: 70%;
                    "
                    id="input-path"
                    placeholder="Input policy file path"
                    readOnly
                    disabled
                    v-model="inputPath"
                />
            </div>
            <div v-if="documentType == 'CloudFormation'">
                <label for="input-path" style="display: block; margin-top: 15px; margin-bottom: 3px"
                    >CloudFormation Parameter File (Optional)</label
                >
                <input
                    type="text"
                    style="display: flex; box-sizing: border-box; position: relative; margin-bottom: 10px; width: 70%"
                    id="input-path"
                    placeholder="CloudFormation Parameter File Path"
                    v-on:change="setCfnParameterFilePath"
                    v-model="initialData.cfnParameterPath"
                />
            </div>
            <div v-if="documentType == 'Terraform Plan'" style="margin-top: 15px">
                <p>
                    For Terraform Plans, generate terraform plan file and convert the plan files to machine-readable
                    JSON files before running policy checks.
                </p>
                <ol>
                    <li><code>$terraform init</code></li>
                    <li><code>$terraform plan -out tf.plan</code></li>
                    <li><code>$terraform show -json -no-color tf.plan > tf.json</code></li>
                    - For TF 0.12 and prior, use command
                    <code>$terraform show tf.plan > tf.out</code>
                    <li>View the converted JSON file in VS Code and run the desired policy check</li>
                </ol>
            </div>
        </div>
        <hr style="margin-top: 25px" />
        <div class="validate-container">
            <h2 style="border-bottom-style: none">Validate Policies</h2>
            <div style="display: grid">
                <p>
                    Validate your policy against IAM policy grammar and AWS best practices. You can view policy
                    validation check findings that include security warnings, errors, general warnings, and suggestions
                    for your policy. These findings provide actionable recommendations that help you author policies
                    that are functional and conform to security best practices.
                </p>
                <div style="display: grid">
                    <div>
                        <button
                            class="button-theme-primary"
                            v-on:click="runValidator"
                            :disabled="validateButtonDisabled"
                        >
                            Run Policy Validation
                        </button>
                        <div style="margin-top: 5px">
                            <p :style="{ color: validatePolicyResponseColor }">
                                {{ validatePolicyResponse }}
                            </p>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <hr style="margin-top: 25px" />
        <div class="custom-checks-container" v-if="documentType != 'JSON Policy Language'">
            <h2 style="border-bottom-style: none">Custom Policy Checks</h2>
            <div style="display: block">
                <p>
                    Validate your policy against your specified security standards using IAM Access Analyzer custom
                    policy checks. You can check for new access against a reference policy, whether access is granted to
                    a list of IAM actions and/or resource ARNs, or public access to supported resource types.
                </p>
                <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-custom-policy-checks.html"
                    >More about Custom Policy Checks</a
                >
                <div style="justify-content: space-between">
                    <div style="display: flex">
                        <div style="display: block; margin-right: 25px">
                            <label for="select-check-type" style="display: block; margin-top: 15px; margin-bottom: 3px"
                                >Select a Check Type</label
                            >
                            <select id="select-check-type" style="margin-bottom: 5px" v-on:change="setCheckType">
                                <option value="CheckAccessNotGranted">CheckAccessNotGranted</option>
                                <option value="CheckNoNewAccess">CheckNoNewAccess</option>
                                <option value="CheckNoPublicAccess">CheckNoPublicAccess</option>
                            </select>
                        </div>
                        <div
                            style="display: block"
                            v-if="
                                (documentType == 'CloudFormation' || documentType == 'Terraform Plan') &&
                                checkType == 'CheckNoNewAccess'
                            "
                        >
                            <label
                                for="select-reference-type"
                                style="display: block; margin-top: 15px; margin-bottom: 3px"
                                >Select a Reference Policy Type</label
                            >
                            <select
                                id="select-reference-type"
                                v-on:change="setCheckNoNewAccessPolicyType"
                                v-model="checkNoNewAccessPolicyType"
                            >
                                <option value="Identity">Identity</option>
                                <option value="Resource">Resource</option>
                            </select>
                        </div>
                    </div>
                </div>
                <div v-if="checkType == 'CheckNoNewAccess'">
                    <div>
                        <label for="input-path" style="display: block; margin-bottom: 3px"
                            >Provide a reference file containing a JSON Policy Document</label
                        >
                        <input
                            type="text"
                            style="
                                display: flex;
                                box-sizing: border-box;
                                position: relative;
                                margin-bottom: 10px;
                                width: 70%;
                            "
                            id="input-path"
                            placeholder="Enter reference policy document"
                            v-on:change="setCheckNoNewAccessFilePath"
                            v-model="initialData.checkNoNewAccessFilePath"
                        />
                    </div>
                    <div style="margin-top: 5px" v-if="initialData.customChecksFileErrorMessage">
                        <p style="color: var(--vscode-errorForeground)">
                            {{ initialData.customChecksFileErrorMessage }}
                        </p>
                    </div>
                    <div>
                        <label style="margin-bottom: 3px">Enter a JSON Policy Document</label>
                        <textarea
                            style="
                                width: 100%;
                                margin-bottom: 10px;
                                font-family:
                                    -apple-system,
                                    BlinkMacSystemFont,
                                    Segoe UI,
                                    Roboto,
                                    Helvetica,
                                    Arial,
                                    sans-serif,
                                    Apple Color Emoji,
                                    Segoe UI Emoji,
                                    Segoe UI Symbol;
                            "
                            rows="30"
                            v-model="initialData.checkNoNewAccessTextArea"
                            v-on:change="setCheckNoNewAccessTextArea"
                            placeholder="Reference policy document"
                        ></textarea>
                    </div>
                </div>
                <div v-if="checkType == 'CheckAccessNotGranted'">
                    <div>
                        <label for="input-path" style="display: block; margin-bottom: 3px">
                            Provide a reference JSON file containing actions and/or resources. The file should be
                            structured as follows:
                            <code
                                >{"actions": ["action1", "action2", "action3"], "resources": ["resource1", "resource2",
                                "resource3"]}</code
                            >
                        </label>
                        <input
                            type="text"
                            style="
                                display: flex;
                                box-sizing: border-box;
                                position: relative;
                                margin-bottom: 10px;
                                width: 70%;
                            "
                            id="input-path"
                            placeholder="File path to JSON file"
                            v-on:change="setCheckAccessNotGrantedFilePath"
                            v-model="initialData.checkAccessNotGrantedFilePath"
                        />
                    </div>
                    <div style="margin-top: 5px" v-if="initialData.customChecksFileErrorMessage">
                        <p style="color: var(--vscode-errorForeground)">
                            {{ initialData.customChecksFileErrorMessage }}
                        </p>
                    </div>
                    <div>
                        <label style="margin-bottom: 3px">Enter a comma-separated list of actions</label>
                        <textarea
                            style="
                                width: 100%;
                                margin-bottom: 10px;
                                font-family:
                                    -apple-system,
                                    BlinkMacSystemFont,
                                    Segoe UI,
                                    Roboto,
                                    Helvetica,
                                    Arial,
                                    sans-serif,
                                    Apple Color Emoji,
                                    Segoe UI Emoji,
                                    Segoe UI Symbol;
                            "
                            rows="15"
                            v-on:change="setcheckAccessNotGrantedActionsTextArea"
                            v-model="initialData.checkAccessNotGrantedActionsTextArea"
                            placeholder="List of actions"
                        ></textarea>
                    </div>
                    <div>
                        <label style="margin-bottom: 3px">Enter a comma-separated list of resource ARNs</label>
                        <textarea
                            style="
                                width: 100%;
                                margin-bottom: 10px;
                                font-family:
                                    -apple-system,
                                    BlinkMacSystemFont,
                                    Segoe UI,
                                    Roboto,
                                    Helvetica,
                                    Arial,
                                    sans-serif,
                                    Apple Color Emoji,
                                    Segoe UI Emoji,
                                    Segoe UI Symbol;
                            "
                            rows="15"
                            v-on:change="setcheckAccessNotGrantedResourcesTextArea"
                            v-model="initialData.checkAccessNotGrantedResourcesTextArea"
                            placeholder="List of resource ARNs"
                        ></textarea>
                    </div>
                </div>
                <div style="display: grid">
                    <b style="margin-bottom: 5px"
                        >A charge is associated with each custom policy check. For more details about pricing, see
                        <a href="https://aws.amazon.com/iam/access-analyzer/pricing/"> IAM Access Analyzer pricing </a>.
                    </b>
                    <div>
                        <button
                            class="button-theme-primary"
                            style="margin-bottom: 5px"
                            v-on:click="runCustomPolicyCheck"
                            :disabled="customCheckButtonDisabled"
                        >
                            Run Custom Policy Check
                        </button>
                        <div style="margin-top: 5px">
                            <p :style="{ color: customPolicyCheckResponseColor }">
                                {{ customPolicyCheckResponse }}
                            </p>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { WebviewClientFactory } from '../../../webviews/client'
import saveData from '../../../webviews/mixins/saveData'
import { IamPolicyChecksWebview } from './iamPolicyChecks'
import { PolicyChecksDocumentType, PolicyChecksPolicyType } from './constants'
import '@../../../resources/css/base.css'
import '@../../../resources/css/securityIssue.css'

const client = WebviewClientFactory.create<IamPolicyChecksWebview>()

export default defineComponent({
    mixins: [saveData],
    data: () => ({
        documentType: 'CloudFormation',
        validatePolicyType: 'Identity',
        checkNoNewAccessPolicyType: 'Identity',
        checkType: 'CheckAccessNotGranted',
        initialData: {
            checkNoNewAccessFilePath: '',
            checkNoNewAccessTextArea: '',
            checkAccessNotGrantedFilePath: '',
            checkAccessNotGrantedActionsTextArea: '',
            checkAccessNotGrantedResourcesTextArea: '',
            customChecksFileErrorMessage: '',
            cfnParameterPath: '',
            pythonToolsInstalled: false,
        },
        inputPath: '',
        checkAccessNotGrantedPathPlaceholder: 'List of actions file path',
        checkAccessNotGrantedActionsTextAreaPlaceholder: 'Enter a list of actions',
        checkAccessNotGrantedResourcesTextAreaPlaceholder: 'Enter a list of resource ARNs',
        validatePolicyResponse: '',
        validatePolicyResponseColor: 'var(--vscode-errorForeground)',
        customPolicyCheckResponse: '',
        customPolicyCheckResponseColor: 'var(--vscode-errorForeground)',
        validateButtonDisabled: false,
        customCheckButtonDisabled: false,
    }),
    async created() {
        this.initialData = (await client.init()) ?? this.initialData
        client.onChangeInputPath((data: string) => {
            this.inputPath = data
        })
        client.onChangeCheckNoNewAccessFilePath((data: string) => {
            this.initialData.checkNoNewAccessFilePath = data
            client
                .readCustomChecksFile(this.initialData.checkNoNewAccessFilePath)
                .then(response => {
                    this.initialData.checkNoNewAccessTextArea = response
                })
                .catch(err => console.log(err))
        })
        client.onChangeCheckAccessNotGrantedFilePath((data: string) => {
            this.initialData.checkAccessNotGrantedFilePath = data
            client
                .readCustomChecksJsonFile(this.initialData.checkAccessNotGrantedFilePath)
                .then(response => {
                    this.initialData.checkAccessNotGrantedActionsTextArea = response.actions
                        ? response.actions.toString()
                        : ''
                    this.initialData.checkAccessNotGrantedResourcesTextArea = response.resources
                        ? response.resources.toString()
                        : ''
                })
                .catch(err => console.log(err))
        })
        client.onChangeCloudformationParameterFilePath((data: string) => {
            this.initialData.cfnParameterPath = data
        })
        client.onValidatePolicyResponse((data: [string, string]) => {
            this.validatePolicyResponse = data[0]
            this.validatePolicyResponseColor = data[1]
        })
        client.onCustomPolicyCheckResponse((data: [string, string]) => {
            this.customPolicyCheckResponse = data[0]
            this.customPolicyCheckResponseColor = data[1]
        })
        client.onFileReadError((data: string) => {
            this.initialData.customChecksFileErrorMessage = data
        })
    },
    methods: {
        setDocumentType: function (event: any) {
            client.emitUiClick('accessanalyzer_selectDocumentType')
            this.documentType = event.target.value
        },
        setValidatePolicyType: function (event: any) {
            client.emitUiClick('accessanalyzer_selectInputPolicyType')
            this.validatePolicyType = event.target.value
        },
        setCheckNoNewAccessPolicyType: function (event: any) {
            client.emitUiClick('accessanalyzer_selectReferencePolicyType')
            this.checkNoNewAccessPolicyType = event.target.value
        },
        setCheckType: function (event: any) {
            client.emitUiClick('accessanalyzer_selectCustomCheckType')
            this.checkType = event.target.value
        },
        setCheckNoNewAccessFilePath: function (event: any) {
            client.emitUiClick('accessanalyzer_selectCheckNoNewAccessFilePath')
            this.initialData.checkNoNewAccessFilePath = event.target.value
            client
                .readCustomChecksFile(this.initialData.checkNoNewAccessFilePath)
                .then(response => {
                    this.initialData.checkNoNewAccessTextArea = response
                })
                .catch(err => console.log(err))
        },
        setCheckNoNewAccessTextArea: function (event: any) {
            this.initialData.checkNoNewAccessTextArea = event.target.value
            this.initialData.checkNoNewAccessFilePath = ''
        },
        setCheckAccessNotGrantedFilePath: function (event: any) {
            client.emitUiClick('accessanalyzer_selectCheckAccessNotGrantedFilePath')
            this.initialData.checkAccessNotGrantedFilePath = event.target.value
            client
                .readCustomChecksJsonFile(this.initialData.checkAccessNotGrantedFilePath)
                .then(response => {
                    this.initialData.checkAccessNotGrantedActionsTextArea = response.actions
                        ? response.actions.toString()
                        : ''
                    this.initialData.checkAccessNotGrantedResourcesTextArea = response.resources
                        ? response.resources.toString()
                        : ''
                })
                .catch(err => console.log(err))
        },
        setcheckAccessNotGrantedActionsTextArea: function (event: any) {
            this.initialData.checkAccessNotGrantedActionsTextArea = event.target.value
            this.initialData.checkAccessNotGrantedFilePath = ''
        },
        setcheckAccessNotGrantedResourcesTextArea: function (event: any) {
            this.initialData.checkAccessNotGrantedResourcesTextArea = event.target.value
            this.initialData.checkAccessNotGrantedFilePath = ''
        },
        setCfnParameterFilePath: function (event: any) {
            client.emitUiClick('accessanalyzer_selectCfnParameterFilePath')
            this.initialData.cfnParameterPath = event.target.value
        },
        runValidator: async function () {
            this.validateButtonDisabled = true
            client.emitUiClick('accessanalyzer_runValidatePolicy')
            await client.validatePolicy(
                this.documentType as PolicyChecksDocumentType,
                this.validatePolicyType as PolicyChecksPolicyType,
                this.initialData.cfnParameterPath
            )
            this.validateButtonDisabled = false
        },
        runCustomPolicyCheck: async function () {
            this.customCheckButtonDisabled = true
            client.emitUiClick('accessanalyzer_runCustomPolicyCheck')
            if (this.checkType == 'CheckNoNewAccess') {
                await client.checkNoNewAccess(
                    this.documentType as PolicyChecksDocumentType,
                    this.checkNoNewAccessPolicyType as PolicyChecksPolicyType,
                    this.initialData.checkNoNewAccessTextArea,
                    this.initialData.cfnParameterPath
                )
            } else if (this.checkType == 'CheckAccessNotGranted') {
                await client.checkAccessNotGranted(
                    this.documentType as PolicyChecksDocumentType,
                    this.initialData.checkAccessNotGrantedActionsTextArea,
                    this.initialData.checkAccessNotGrantedResourcesTextArea,
                    this.initialData.cfnParameterPath
                )
            } else if (this.checkType == 'CheckNoPublicAccess') {
                await client.checkNoPublicAccess(
                    this.documentType as PolicyChecksDocumentType,
                    this.initialData.cfnParameterPath
                )
            }
            this.customCheckButtonDisabled = false
        },
    },
    computed: {},
})
</script>
